/*
	File:		ObjectManager.cpp
	Author:		dmonroe
	Date:		11/27/2012
	Copyright:	Full Sail University

	Purpose:	Create, store, and maintain game objects
*/

#include "../source/IObjectManager.h"
#include "../source/IEntity.h"

#include <cassert>
#include <list>
#include <map>
using std::list;
using std::map;
using std::pair;


//******************************************************************
// CObjectManager
//	- concrete class for handling game objects
class CObjectManager : public IObjectManager
{
public:
	// SINGLETON helper:
	friend	IObjectManager*		IObjectManager::GetInstance		( void );
	friend	void				IObjectManager::DeleteInstance	( void );

	
	// SETUP
	virtual bool		Initialize			( void );
	virtual bool		Terminate			( void );
	
	// OBJECT FACTORY
	virtual bool		RegisterObject		( unsigned int ID, IEntity* (*pFunction)( void* ) );
	virtual void		UnregisterObject	( unsigned int ID );
	virtual IEntity*	CreateObject		( unsigned int ID, void* pData );
	virtual IEntity*	CreateAndStoreObject( unsigned int ID, void* pData, unsigned int unCategory );

	// OBJECT STORAGE
	virtual void		StoreObject			( IEntity* pObject, unsigned int unCategory );
	virtual void		RemoveObject		( IEntity* pObject, unsigned int unCategory );
	virtual void		RemoveObjects		( unsigned int unCategory );
	virtual void		RemoveAllObjects	( void );

	// OBJECT INTERACTION
	virtual void		InputAllObjects		( float fElapsedTime );
	virtual void		UpdateAllObjects	( float fElapsedTime );
	virtual void		RenderAllObjects	( float fElapsedTime );
	virtual void		ForEachObject		( void (IEntity::*pMethod)( void ) );
	virtual void		CheckCollisions		( unsigned int unCategory1, unsigned int unCategory2, bool bMutualResponse = true );
	virtual void		CheckAllCollisions	( void );
	

	
private:
	// SINGLETON
	static CObjectManager*		s_Instance;	

	CObjectManager				( void );				// Default constructor
	virtual	~CObjectManager		( void );				// Destructor

	CObjectManager				( const CObjectManager& );		// Copy constructor
	CObjectManager&	operator=	( const CObjectManager& );		// Assignment operator

	
	// Wrapper Status
	enum EStatus
	{	
		E_UNINITIALIZED,
		E_INITIALIZED,
		E_DESTROYED
	};
	EStatus						m_eStatus;				// initialization status


	// Factory
	typedef map< unsigned int, IEntity* (*)( void* ) >	TFactoryMap;
	TFactoryMap		m_ObjectCreators;

	// Storage
	void	Synchronize( void );

	typedef map< unsigned int, list< IEntity* > >	TObjectMap;
	typedef pair< unsigned int, IEntity* >			TCategoryObjectPair;
	typedef list< TCategoryObjectPair >				TObjectList;

	TObjectMap		m_Objects;
	TObjectList		m_ObjectAddQueue;
	TObjectList		m_ObjectRemoveQueue;
};
//******************************************************************



//******************************************************************
// SINGLETON

// Instantiate static pointer to null (no instance yet)
/*static*/ CObjectManager*		CObjectManager::s_Instance		= NULL;

// Interface singleton accessor
/*static*/ IObjectManager*		IObjectManager::GetInstance		( void )
{
	// Allocate singleton on first use
	if( CObjectManager::s_Instance == NULL )
		CObjectManager::s_Instance = new CObjectManager;

	// Return the singleton
	return CObjectManager::s_Instance;
}

// Interface singleton destructor
/*static*/ void		IObjectManager::DeleteInstance	( void )
{
	// Deallocate singleton
	delete CObjectManager::s_Instance;
	CObjectManager::s_Instance = NULL;
}
//******************************************************************



//******************************************************************
// CONSTRUCTOR
CObjectManager::CObjectManager(void)
{
	// Set data members to 'clean' values
	m_eStatus = E_UNINITIALIZED;
}
//******************************************************************



//******************************************************************
// DESTRUCTOR
/*virtual*/	CObjectManager::~CObjectManager(void)
{
	// Release memory cleanly in Release mode
#if !defined( DEBUG ) && !defined( _DEBUG )
	if( m_eStatus == E_INITIALIZED )
		Terminate();
#endif
}
//******************************************************************



//******************************************************************
// INITIALIZE
/*virtual*/	bool CObjectManager::Initialize( void )
{
	// Sanity-check the object manager's status
	assert( m_eStatus == E_UNINITIALIZED && "CObjectManager::Initialize - object manager has already been initialized" != 0 );

	
	// Success!
	m_eStatus = E_INITIALIZED;
	return true;
}
//******************************************************************



//******************************************************************
// TERMINATE
/*virtual*/	bool CObjectManager::Terminate( void )
{
	// Sanity-check the object manager's status
	assert( m_eStatus == E_INITIALIZED && "CObjectManager::Terminate - object manager has not been initialized" != 0 );

	// Release all referenced objects
	RemoveAllObjects();

	for( TObjectList::iterator iter = m_ObjectAddQueue.begin(); iter != m_ObjectAddQueue.end(); ++iter )
		(*iter).second->Release();

	// Empty the queues
	m_ObjectAddQueue.clear();
	m_ObjectRemoveQueue.clear();

	// Unregister all types
	m_ObjectCreators.clear();

	// Wrapper is now destroyed
	m_eStatus = E_DESTROYED;
	return true;
}
//******************************************************************



//******************************************************************
// REGISTER OBJECT
/*virtual*/ bool		CObjectManager::RegisterObject		( unsigned int ID, IEntity* (*pFunction)( void* ) )
{
	// Sanity-check the object manager's status
	assert( m_eStatus == E_INITIALIZED && "CObjectManager::RegisterObject - object manager has not been initialized" != 0 );

	// Validate the callback function parameter
	assert( pFunction != NULL && "CObjectManager::RegisterObject - callback function cannot be null" != 0 );
		

	// Check if the state has already been registered
	if( m_ObjectCreators[ ID ] != NULL && m_ObjectCreators[ ID ] != pFunction )
	{
		// MESSAGE
		return false;
	}


	// Add the function pointer to the map
	m_ObjectCreators[ ID ] = pFunction;

	// Success!
	return true;
}
//******************************************************************



//******************************************************************
// UNREGISTER OBJECT
/*virtual*/ void		CObjectManager::UnregisterObject	( unsigned int ID )
{
	// Sanity-check the object manager's status
	assert( m_eStatus == E_INITIALIZED && "CObjectManager::UnregisterObject - object manager has not been initialized" != 0 );

	// Remove the function pointer from the map
	m_ObjectCreators[ ID ] = NULL;
}
//******************************************************************



//******************************************************************
// CREATE OBJECT
/*virtual*/ IEntity*	CObjectManager::CreateObject		( unsigned int ID, void* pData )
{
	// Sanity-check the object manager's status
	assert( m_eStatus == E_INITIALIZED && "CObjectManager::CreateObject - object manager has not been initialized" != 0 );

	// Validate the creator function
	assert( m_ObjectCreators[ ID ] != NULL && "CObjectManager::CreateObject - object type ID does not have a registered factory creator!" != 0 );

	// Return the result of the creator function
	return m_ObjectCreators[ ID ]( pData );
}
//******************************************************************



//******************************************************************
// CREATE AND STORE OBJECT
/*virtual*/ IEntity*	CObjectManager::CreateAndStoreObject( unsigned int ID, void* pData, unsigned int unCategory )
{
	// Attempt to create the object
	IEntity* pEntity = CreateObject( ID, pData );

	// Store the result
	StoreObject( pEntity, unCategory );

	// Release the creation reference
	pEntity->Release();

	// Return the result (WITHOUT a reference)
	return pEntity;
}
//******************************************************************



//******************************************************************
// STORE OBJECT
/*virtual*/ void		CObjectManager::StoreObject			( IEntity* pObject, unsigned int unCategory )
{
	// Sanity-check the object manager's status
	assert( m_eStatus == E_INITIALIZED && "CObjectManager::StoreObject - object manager has not been initialized" != 0 );

	// Validate the pointer parameter
	assert( pObject != NULL && "CObjectManager::StoreObject - cannot store a null parameter" != 0 );

	// Add the new object to the queue
	m_ObjectAddQueue.push_back( TCategoryObjectPair( unCategory, pObject ) );

	// Mark the reference to the object
	pObject->AddRef();
}
//******************************************************************



//******************************************************************
// REMOVE OBJECT
/*virtual*/ void		CObjectManager::RemoveObject		( IEntity* pObject, unsigned int unCategory )
{
	// Sanity-check the object manager's status
	assert( m_eStatus == E_INITIALIZED && "CObjectManager::RemoveObject - object manager has not been initialized" != 0 );
	
	// Validate the pointer parameter
	assert( pObject != NULL && "CObjectManager::RemoveObject - cannot remove a null parameter" != 0 );

	// Add the object to the remove queue
	m_ObjectRemoveQueue.push_back( TCategoryObjectPair( unCategory, pObject ) );
}
//******************************************************************



//******************************************************************
// REMOVE OBJECTS
/*virtual*/ void		CObjectManager::RemoveObjects		( unsigned int unCategory )
{
	// Sanity-check the object manager's status
	assert( m_eStatus == E_INITIALIZED && "CObjectManager::RemoveObjects - object manager has not been initialized" != 0 );

	// Add NULL to the remove queue to indicate complete removal
	m_ObjectRemoveQueue.push_back( TCategoryObjectPair( unCategory, (IEntity*)NULL ) );
}
//******************************************************************



//******************************************************************
// REMOVE ALL OBJECTS
/*virtual*/ void		CObjectManager::RemoveAllObjects	( void )
{
	// Sanity-check the object manager's status
	assert( m_eStatus == E_INITIALIZED && "CObjectManager::RemoveAllObjects - object manager has not been initialized" != 0 );

	// Ignore the removal queue. This method *should not* be called
	// during an other iterations.
	for( TObjectMap::iterator mapIter = m_Objects.begin(); mapIter != m_Objects.end(); ++mapIter )
		for( list< IEntity* >::iterator iter = mapIter->second.begin(); iter != mapIter->second.end(); ++iter )
			(*iter)->Release();

	m_Objects.clear();
}
//******************************************************************



//******************************************************************
// INPUT ALL OBJECTS
/*virtual*/ void		CObjectManager::InputAllObjects		( float fElapsedTime )
{
	Synchronize();

	// Call the Input method of EVERY object in the map
	for( TObjectMap::iterator mapIter = m_Objects.begin(); mapIter != m_Objects.end(); ++mapIter )
		for( list< IEntity* >::iterator iter = mapIter->second.begin(); iter != mapIter->second.end(); ++iter )
			(*iter)->Input( fElapsedTime );

	Synchronize();
}
//******************************************************************



//******************************************************************
// UPDATE ALL OBJECTS
/*virtual*/ void		CObjectManager::UpdateAllObjects	( float fElapsedTime )
{
	Synchronize();

	// Call the Update method of EVERY object in the map
	for( TObjectMap::iterator mapIter = m_Objects.begin(); mapIter != m_Objects.end(); ++mapIter )
		for( list< IEntity* >::iterator iter = mapIter->second.begin(); iter != mapIter->second.end(); ++iter )
			(*iter)->Update( fElapsedTime );

	Synchronize();
}
//******************************************************************



//******************************************************************
// RENDER ALL OBJECTS
/*virtual*/ void		CObjectManager::RenderAllObjects	( float fElapsedTime )
{
	Synchronize();

	// Call the Render method of EVERY object in the map
	for( TObjectMap::iterator mapIter = m_Objects.begin(); mapIter != m_Objects.end(); ++mapIter )
		for( list< IEntity* >::iterator iter = mapIter->second.begin(); iter != mapIter->second.end(); ++iter )
			(*iter)->Render( fElapsedTime );

	Synchronize();
}
//******************************************************************



//******************************************************************
// FOR EACH OBJECT
/*virtual*/ void		CObjectManager::ForEachObject		( void (IEntity::*pMethod)( void ) )
{
	Synchronize();

	// Call the method parameter of EVERY object in the map
	for( TObjectMap::iterator mapIter = m_Objects.begin(); mapIter != m_Objects.end(); ++mapIter )
		for( list< IEntity* >::iterator iter = mapIter->second.begin(); iter != mapIter->second.end(); ++iter )
			((*iter)->*pMethod)( );

	Synchronize();
}
//******************************************************************



//******************************************************************
// CHECK COLLISIONS
/*virtual*/ void		CObjectManager::CheckCollisions		( unsigned int unCategory1, unsigned int unCategory2, bool bMutualResponse )
{	
	Synchronize();

	// Check collisions between objects of the two categories
	list< IEntity* >& categoryList1 = m_Objects[ unCategory1 ];
	list< IEntity* >& categoryList2 = m_Objects[ unCategory2 ];


	// Should they both respond to collision?
	if( bMutualResponse == true )
	{
		for( list< IEntity* >::iterator iter1 = categoryList1.begin(); iter1 != categoryList1.end(); ++iter1 )
		{
			// Can the first object even collide?
			if( (*iter1)->IsCollidable() == false )
				continue;

			for( list< IEntity* >::iterator iter2 = categoryList2.begin(); iter2 != categoryList2.end(); ++iter2 )
			{
				// Can the second object collide & are they colliding?
				if( (*iter1) != (*iter2) 
					&& (*iter2)->IsCollidable() == true 
					&& IsColliding( (*iter1), (*iter2) ) == true )
				{
					// Each responds to the collision
					(*iter2)->OnCollision( (*iter1) );
					if( (*iter1)->OnCollision( (*iter2) ) == false )
						break;
				}
			}
		}
	}
	else
	{
		for( list< IEntity* >::iterator iter1 = categoryList1.begin(); iter1 != categoryList1.end(); ++iter1 )
		{
			// Can the first object even collide?
			if( (*iter1)->IsCollidable() == false )
				continue;

			for( list< IEntity* >::iterator iter2 = categoryList2.begin(); iter2 != categoryList2.end(); ++iter2 )
			{
				// Can the second object collide & are they colliding?
				if( (*iter1) != (*iter2) 
					&& (*iter2)->IsCollidable() == true 
					&& IsColliding( (*iter1), (*iter2) ) == true )
				{
					// Only the first responds to collision
					if( (*iter1)->OnCollision( (*iter2) ) == false )
						break;
				}
			}
		}
	}

	Synchronize();
}
//******************************************************************



//******************************************************************
// CHECK ALL COLLISIONS
/*virtual*/ void		CObjectManager::CheckAllCollisions	( void )
{
	Synchronize();
	

	// Iterate through the map
	for( TObjectMap::iterator mapIter1 = m_Objects.begin(); mapIter1 != m_Objects.end(); ++mapIter1 )
	{
		TObjectMap::iterator mapIter2 = mapIter1;

		// Objects in a category cannot collide with each other
		for( ++mapIter2; mapIter2 != m_Objects.end(); ++mapIter2 )
		{
			// Check collisions between objects of the two categories
			list< IEntity* >& categoryList1 = mapIter1->second;
			list< IEntity* >& categoryList2 = mapIter1->second;
			
			for( list< IEntity* >::iterator iter1 = categoryList1.begin(); iter1 != categoryList1.end(); ++iter1 )
			{
				// Can the first object even collide?
				if( (*iter1)->IsCollidable() == false )
					continue;

				for( list< IEntity* >::iterator iter2 = categoryList2.begin(); iter2 != categoryList2.end(); ++iter2 )
				{
					// Can the second object collide & are they colliding?
					if( (*iter1) != (*iter2) 
						&& (*iter2)->IsCollidable() == true 
						&& IsColliding( (*iter1), (*iter2) ) == true )
					{
						// Only the first responds to collision
						if( (*iter1)->OnCollision( (*iter2) ) == false )
							break;
					}
				}
			}
		}
	}

	Synchronize();
}
//******************************************************************



//******************************************************************
// SYNCHRONIZE
void CObjectManager::Synchronize( void )
{
	// Add the queued objects to the map
	for( TObjectList::iterator iter = m_ObjectAddQueue.begin(); iter != m_ObjectAddQueue.end(); ++iter )
		m_Objects[ (*iter).first ].push_back( (*iter).second );

	m_ObjectAddQueue.clear();


	// Remove the queued objects from the map
	for( TObjectList::iterator iter = m_ObjectRemoveQueue.begin(); iter != m_ObjectRemoveQueue.end(); ++iter )
	{
		list< IEntity* >& categoryList = m_Objects[ (*iter).first ];

		// Remove all objects of that category?
		if( (*iter).second == NULL )
		{
			for( list< IEntity* >::iterator catIter = categoryList.begin(); catIter != categoryList.end(); ++catIter )
				(*catIter)->Release();

			categoryList.clear();
		}
		else
		{
			// Find & remove the one object
			for( list< IEntity* >::iterator catIter = categoryList.begin(); catIter != categoryList.end(); ++catIter )
			{
				if( (*catIter) == (*iter).second )
				{
					(*catIter)->Release();
					catIter = categoryList.erase( catIter );
					break;
				}
			}
		}
	}

	m_ObjectRemoveQueue.clear();
}
//******************************************************************
